/**
 * 
 */
package com.netscenery.yang.model;

import java.util.ArrayList;

import org.jdom2.Attribute;
import org.jdom2.Element;

import com.netscenery.util.ErrTag;
import com.netscenery.util.XMLTag;
import com.netscenery.yang.YangKeyword;
import com.netscenery.yang.YangStatement;
import com.netscenery.yang.parser.YangParserException;

/**
 * @author llly
 *
 */
public class Bits extends Type{
	private ArrayList<Bit> bits;
	private int curHighestPos;
	
	public Bits(){

	}
//	/**
//	 * �̳�һ��Bits
//	 * @param derivedBits
//	 */
//	public boolean derived(Bits derivedBits){
//		if (null == derivedBits){
//			return false;
//		}
//		
//		if (null != bits){
//			return false;
//		}
//		isDerivedType = true;
//		bits.addAll(derivedBits.bits);
//		curHighestPos = derivedBits.curHighestPos;
//		return true;
//	}
	public int getBitActualPos(String bitName){
		if (null == bitName){
			return -1;
		}
		int curHighValue = 0;
		boolean first = false;
		if (null != bits){
			int size = bits.size();
			for (int i = 0; i < size; i++){
				Bit bit = bits.get(i);
				if (null != bit ){
					if (bit.isUsedefaultpos()){
						if (false == first){
							first = true;
							curHighValue = 0;
						}
						else {
							curHighValue++;
						}
					}
					else {
						if (bit.getPosition() > curHighValue){
							curHighValue = bit.getPosition();
						}
					}
					if (bitName.equals(bit.getName())){
						if(bit.isUsedefaultpos()){
							return curHighValue;
						}
						else {
							return bit.getPosition();
						}
					}
				}
			}
		}
		return -1;
	}
	/**
	 * ��Bits�����һ��λ
	 * @param bit
	 */
	public boolean addBit(Bit bit){
		//������Զ�������ͣ��������ٶ���bit
		if (this.isDerivedType == true){
			return false;
		}
		if (null == bit){
			return false;
		}
		//λ��Ʋ����ظ�
		if (null != getBit(bit.getName())){
			return false;
		}
		//λ�ò����ظ�
		if ( !bit.isUsedefaultpos()){
			if (null != bits){
				int size = bits.size();
				for (int i = 0; i < size; i++){
					Bit e = bits.get(i);
					if (null != e){
						//���ܺ�����λ�Ѿ������ʵ�ʵ�λ��һ��
						if (bit.getPosition() == getBitActualPos(e.getName())){
							return false;
						}
					}
				}
			}
		}
		//���ǰ����λ����4294967295�����������Ĭ��λ�õ�BitԪ��
		if(-1 == this.curHighestPos){
			if (true == bit.isUsedefaultpos()){
				return false;
			}
		}
		if (null == this.bits){
			this.bits = new ArrayList<Bit>(1);
		}
		/*���ǰbits��û�ж����κ�λ�������ʹ��Ĭ��λ�ã���ʵ��λ��Ϊ0*/
		if (true == bit.isUsedefaultpos()){
			if (0 == bits.size()){
				this.curHighestPos = 0;
			}
			else {
				this.curHighestPos++;
			}			
		}
		else {
			if (bit.getPosition() > this.curHighestPos){
				this.curHighestPos = bit.getPosition();
			}
			
		}


		return bits.add(bit);

	}
	/**
	 * ��Bits�����ָ����Ƶ�λ��λ��ΪĬ��λ��
	 * @param bitName
	 */
	public boolean addBit(String bitName){

		if (null == bitName){
			return false;
		}
		//��Ʋ����ظ�
		if (null != bits){
			int size = bits.size();
			for (int i = 0; i < size; i++){
				Bit bitTemp = bits.get(i);
				if (null != bitTemp){
					//bit�����ظ�
					if (bitName.equals(bitTemp.name)){
						return false;
					}
				}
			}
		}
		
		Bit bit = new Bit(bitName);
		addBit(bit);
		return true;
	}
	/**
	 * ��Bits�����ָ����ơ�ָ��λ�õ�λ
	 * @param bitName
	 * @param position
	 */
	public boolean addBit(String bitName,int position){
		if (null == bitName){
			return false;
		}

		
		if (null != bits){
			int size = bits.size();
			for (int i = 0; i < size; i++){
				Bit bitTemp = bits.get(i);
				if (null != bitTemp){
					//bit�����ظ�
					if (bitName.equals(bitTemp.name)){
						return false;
					}
					//λ�ò����ظ�
					if (position == bitTemp.getPosition()){
						return false;
					}
				}
			}
		}
		Bit bit = new Bit(bitName,position);
		
		addBit(bit);
		return true;
	}
	/**
	 * ��ȡĿǰBits�д��ڶ����Ѷ����λ
	 * @return
	 */
	public int getBitsNumber(){
		if (null == this.bits){
			return 0;
		}
		return this.bits.size();
	}
	/**
	 * ��������ȡλ��Ϣ
	 * @param index
	 * @return
	 */
	public Bit getBit(int index){
		if (null == this.bits){
			return null;
		}
		
		if (index >= this.bits.size()){
			return null;
		}
		return this.bits.get(index);

	}
	/**
	 * ���λ����Ƶõ�λ����Ϣ
	 * @param bitName
	 * @return
	 */
	public Bit getBit(String bitName){
		if (null == this.bits){
			return null;
		}
		for (int i = 0; i < this.bits.size();i++){
			Bit bit = this.bits.get(i);
			if (null != bit){
				if (bitName.equals(bit.name)){
					return bit;
				}
			}
		}
		
		
		return null;
	}
	/**
	 * ɾ��ָ����λ�������ظ�λ����Ϣ
	 * @param bitName
	 */
	public Bit removeBit(String bitName){
		if(null == this.bits){
			return null;
		}
		
		int size = this.bits.size();
		for (int i = 0; i < size; i++){
			Bit bit = getBit(i);
			if (null != bit){
				if (bitName.equals(bit.name)){
					return this.bits.remove(i);
				}
			}
		}
		
		return null;
	}
	
	public Bit removeBit(int index){
		if(null == this.bits){
			return null;
		}
		return bits.remove(index);

	}
	
	/**
	 * ���λ�б���û��ָ����Ƶ�λ���򷵻�-1�����򷵻�����
	 * @param bitName
	 * @return
	 */
	public int getBitIndex(String bitName){
		if(null == this.bits){
			return -1;
		}
		
		int size = this.bits.size();
		for (int i = 0; i < size; i++){
			Bit bit = getBit(i);
			if (null != bit){
				if (bitName.equals(bit.name)){
					return i;
				}
			}
		}
		
		return -1;
	}
//	/**
//	 * ��ȡָ��λ����ʵ��λ�ã�����б���û��ָ����λ���򷵻�-1
//	 * @param bitName
//	 * @return
//	 */
//	public int getBitActualPosition(int index){
//		Bit bit = getBit(index);
//		if (null == bit){
//			return -1;
//		}
//		return bit.getActualPostion();
//	}
//	
//	/**
//	 * ��ȡָ��λ����ʵ��λ�ã�����б���û��ָ����λ���򷵻�-1
//	 * @param bitName
//	 * @return
//	 */
//	public int getBitActualPosition(String bitName){
//		int index = getBitIndex(bitName);
//		if (-1 == index){
//			return -1;
//		}
//		
//		return getBitActualPosition(index);
//		
//
//	}
	public boolean exchangeBits(int index1, int index2){
		Bit bit1 = getBit(index1);
		Bit bit2 = getBit(index2);
		
		if (null == bit1 || null == bit2){
			return false;
		}
		
		this.bits.set(index1, bit2);
		this.bits.set(index2, bit1);
		int actualPos1 = bit1.getActualPostion();
		int actualPos2 = bit2.getActualPostion();
		//˫������Ĭ�ϵģ�ֻҪ����˫����ʵ��λ�ü���
		if ((bit1.isUsedefaultpos() == true )
				&&(bit2.isUsedefaultpos() == true)){
			bit1.setActualPostion(actualPos2);
			bit2.setActualPostion(actualPos1);
		}
		//˫��������Ĭ�ϵģ���ʲô����Ҫ����
		else if ((bit1.isUsedefaultpos() == false )
				&&(bit2.isUsedefaultpos() == false)){
			
		}
		//�����Ҫ��С��index��ʼ���¼���ʵ��λ��
		else{
			//TODO:��ʵ��
		}
		return true;
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.Type#validate()
	 */
	@Override
	public Validator validate() {

		Validator v = new Validator();
		if (this.isDerivedType()){
			if (0 != this.getBitsNumber()){
				v.setIsValid(false);
				v.addMsg(new Message(this, ErrTag.ERROR," a bits type can not be restricted."));
			}
		}
		else {
			if (0 == this.getBitsNumber()){
				v.setIsValid(false);
				v.addMsg(new Message(this, ErrTag.ERROR," a builtin-type bits must has at least one bit."));
	
			}
		}
		
		int size = getBitsNumber();
		for (int i = 0; i < size; i++){
			Bit bit = getBit(i);
			if (null == bit){
				continue;
			}
			Validator bValidator = bit.validate();
			v.copy(bValidator);
		}
		
		Validator sValidator = super.validate();
		v.copy(sValidator);
		return v;
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.ModelElement#toYang()
	 */
	@Override
	public YangStatement toYang() {
		YangStatement bits = new YangStatement(YangStatement.YANG_STATEMENT_TYPE,this.name);
		if (0 != this.getBitsNumber()){
			for (int i = 0; i < this.getBitsNumber();i++){
				Bit bit = getBit(i);
				if (null != bit){
					YangStatement bitStatement = bit.writeYang();
					bits.addChild(bitStatement);
				}
			}
		}
		return bits;
	}
	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.ModelElement#toYang()
	 */
	@Override
	public Element toYin() {
		Element bits = new Element(YangKeyword.TYPE.getKeyword(),env.getNs());
		Attribute name = new Attribute(XMLTag.XML_TAG_NAME,this.name);
		bits.setAttribute(name);
		if (0 != this.getBitsNumber()){
			for (int i = 0; i < this.getBitsNumber();i++){
				Bit bit = getBit(i);
				if (null != bit){
					Element bitStatement = bit.writeYin();
					bits.addContent(bitStatement);
				}
			}
		}
		return bits;
	}
	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.ModelElement#toXML()
	 */
	@Override
	public Element toXML() {
		Element bits = new Element(XMLTag.XML_TAG_TYPE);
		
		Element type;
		if (this.isDerivedType() == true){
			type = new Element(	XMLTag.XML_TAG_DERIVEDTYPE);
		}
		else {
			type = new Element(XMLTag.XML_TAG_BASETYPE);
		}
		
		type.setText(this.name);
		bits.addContent(type);
		
		if (0 != this.getBitsNumber()){
			for (int i = 0; i < this.getBitsNumber();i++){
				Bit bit = getBit(i);
				if (null != bit){
					bit.setActualPostion(getBitActualPos(bit.getName()));
					Element bitElement = bit.writeXML();
					bits.addContent(bitElement);
				}
			}
		}
		return bits;
	}


	public int readXML(Element element) throws ModelException {
		if (null == element){
			return MODELELEMENT_OK;
		}
	
		if (element.getName().equals(XMLTag.XML_TAG_BIT)){
			Env subEnv = newSubEnv();
			Bit bit = (Bit)(ModelElement.parseXML(subEnv, element));
			this.addBit(bit);
		}
		else if (element.getName().equals(XMLTag.XML_TAG_NAME)){
			return MODELELEMENT_OK;
		}
		else {
			return MODELELEMENT_FAIL;
		}
		

		return MODELELEMENT_OK;

	}
	
	public int readYANG(YangStatement statement) throws YangParserException, ModelException {
		if (null == statement){
			return MODELELEMENT_OK;
		}
		if (statement.getKeyword().equals(YangStatement.YANG_STATEMENT_BIT)){
			Env subEnv = newSubEnv();
			Bit bit = (Bit)(ModelElement.parseYANG(subEnv, statement));
			this.addBit(bit);
		}
		else {
			return MODELELEMENT_FAIL;
		}
		

		return MODELELEMENT_OK;
	}
	public int readYin(Element element) throws ModelException {
		if (null == element){
			return MODELELEMENT_OK;
		}
		if (element.getName().equals(YangKeyword.BIT.getKeyword())){
			Env subEnv = newSubEnv();
			Bit bit = (Bit)(ModelElement.parseYin(subEnv, element));
			this.addBit(bit);
		}
		else {
			return MODELELEMENT_FAIL;
		}
		

		return MODELELEMENT_OK;
	}
	public boolean checkValue(String value){
		if (null == value){
			return false;
		}
		
		String[] bits = value.split(" ");
		
		int size = bits.length;
		
		for (int i=0; i<size;i++){
			String str = bits[i];
			str = str.trim();
			if (0 == str.length()){
				continue;
			}
			Type baseType = this.getBaseType();
			if (null == ((Bits)baseType).getBit(str)){
				return false;
			}
		}
		return true;
		
	}
}
